/**********************************************************************
* $Id$		lpc177x_8x_emac.c			2011-06-02
*//**
* @file		lpc177x_8x_emac.c
* @brief	Contains all functions support for Ethernet MAC firmware
*			library on LPC177x_8x
* @version	1.0
* @date		02. June. 2011
* @author	NXP MCU SW Application Team
* 
* Copyright(C) 2011, NXP Semiconductor
* All rights reserved.
*
***********************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors'
* relevant copyright in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers.  This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
**********************************************************************/
#ifdef __BUILD_WITH_EXAMPLE__
#include "lpc177x_8x_libcfg.h"
#else
#include "lpc177x_8x_libcfg_default.h"
#endif /* __BUILD_WITH_EXAMPLE__ */
#ifdef _EMAC

#include "lpc177x_8x_emac.h"
#include "lpc177x_8x_clkpwr.h"
#include "lpc177x_8x_pinsel.h"

/* Peripheral group ----------------------------------------------------------- */
/** @addtogroup EMAC
 * @{
 */
 
/************************** PRIVATE VARIABLES *************************/


/* MII Mgmt Configuration register - Clock divider setting */
const uint8_t EMAC_clkdiv[] = { 4, 6, 8, 10, 14, 20, 28, 36, 40, 44, 48, 52, 56, 60, 64 };

/* EMAC Config data */
static EMAC_CFG_Type EMAC_Configs;


/* EMAC local DMA Descriptors */


#ifdef __IAR_SYSTEMS_ICC__
/* Global Tx Buffer data */
#pragma data_alignment=4
static uint16_t saFrameBuffers[EMAC_MAX_FRAME_NUM][EMAC_MAX_FRAME_SIZE];
#else
/* Global Rx Buffer data */
static uint16_t __attribute__ ((aligned (4))) saFrameBuffers[EMAC_MAX_FRAME_NUM][EMAC_MAX_FRAME_SIZE];
#endif
static uint32_t sulCurrFrameSz = 0;
static uint8_t  sbCurrFrameID = 0;

/***************************** PRIVATE FUNCTION *****************************/
static void EMAC_UpdateRxConsumeIndex(void);
static void EMAC_UpdateTxProduceIndex(void);
static uint32_t EMAC_AllocTxBuff(uint16_t nFrameSize, uint8_t bLastFrame);
static uint32_t EMAC_GetRxFrameSize(void);


/*********************************************************************//**
 * @brief
 * @param[in]
 * @return
 **********************************************************************/
void rx_descr_init (void)
{
	unsigned int i;

	for (i = 0; i < EMAC_NUM_RX_FRAG; i++)
	{
		RX_DESC_PACKET(i)  = RX_BUF(i);
		RX_DESC_CTRL(i)    = EMAC_RCTRL_INT | (EMAC_ETH_MAX_FLEN-1);
		RX_STAT_INFO(i)    = 0;
		RX_STAT_HASHCRC(i) = 0;
	}

	/* Set EMAC Receive Descriptor Registers. */
	LPC_EMAC->RxDescriptor    = RX_DESC_BASE;
	LPC_EMAC->RxStatus        = RX_STAT_BASE;
	LPC_EMAC->RxDescriptorNumber = EMAC_NUM_RX_FRAG-1;

	/* Rx Descriptors Point to 0 */
	LPC_EMAC->RxConsumeIndex  = 0;
}

/*********************************************************************//**
 * @brief
 * @param[in]
 * @return
 **********************************************************************/
void tx_descr_init (void)
{
	unsigned int i;

	for (i = 0; i < EMAC_NUM_TX_FRAG; i++)
	{
		TX_DESC_PACKET(i) = TX_BUF(i);
		TX_DESC_CTRL(i)   = 0;
		TX_STAT_INFO(i)   = 0;
	}

	/* Set EMAC Transmit Descriptor Registers. */
	LPC_EMAC->TxDescriptor    = TX_DESC_BASE;
	LPC_EMAC->TxStatus        = TX_STAT_BASE;
	LPC_EMAC->TxDescriptorNumber = EMAC_NUM_TX_FRAG-1;

	/* Tx Descriptors Point to 0 */
	LPC_EMAC->TxProduceIndex  = 0;
}

/*********************************************************************//**
 * @brief		Set Station MAC address for EMAC module
 * @param[in]	abStationAddr Pointer to Station address that contains 6-bytes
 * 				of MAC address.
 * @return		None
 **********************************************************************/
void setEmacAddr(uint8_t abStationAddr[])
{
	/* Set the Ethernet MAC Address registers */
	LPC_EMAC->SA0 = ((uint32_t)abStationAddr[5] << 8) | (uint32_t)abStationAddr[4];
	LPC_EMAC->SA1 = ((uint32_t)abStationAddr[3] << 8) | (uint32_t)abStationAddr[2];
	LPC_EMAC->SA2 = ((uint32_t)abStationAddr[1] << 8) | (uint32_t)abStationAddr[0];
}


/************************** GLOBAL/PUBLIC FUNCTIONS *************************/

/** @defgroup EMAC_Public_Functions
 * @{
 */

/*********************************************************************//**
 * @brief          Write data to PHY
 * @param[in]  PhyReg    PHY register address
 * @param[in]  Value       Register Value
 * @return        None
 **********************************************************************/
void EMAC_Write_PHY (uint8_t PhyReg, uint16_t Value)
{
	unsigned int tout;

	LPC_EMAC->MADR = ((EMAC_Configs.bPhyAddr & 0x1F) << 8 )| (PhyReg & 0x1F);
	LPC_EMAC->MWTD = Value;

	/* Wait utill operation completed */
	tout = 0;

	for (tout = 0; tout < EMAC_MII_WR_TOUT; tout++)
	{
		if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0)
		{
			break;
		}
	}
}

/*********************************************************************//**
 * @brief          Read data from PHY register
 * @param[in]  PhyReg    PHY register address
 * @return        Register value
 **********************************************************************/
uint16_t EMAC_Read_PHY (uint8_t PhyReg)
{
	unsigned int tout;

	LPC_EMAC->MADR = ((EMAC_Configs.bPhyAddr & 0x1F) << 8 )| (PhyReg & 0x1F);
	LPC_EMAC->MCMD = EMAC_MCMD_READ;

	/* Wait until operation completed */
	tout = 0;
	for (tout = 0; tout < EMAC_MII_RD_TOUT; tout++)
	{
		if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0)
		{
			break;
		}
	}

	LPC_EMAC->MCMD = 0;
	return (LPC_EMAC->MRDD);
}

/*********************************************************************//**
 * @brief         Set Full/Half Duplex Mode
 * @param[in]  full_duplex    0: Half-duplex, 1: Full-duplex
 * @return        None
 **********************************************************************/
void EMAC_SetFullDuplexMode(uint8_t full_duplex)
{
  if(full_duplex)
  {
        LPC_EMAC->MAC2    |= EMAC_MAC2_FULL_DUP;
	LPC_EMAC->Command |= EMAC_CR_FULL_DUP;
	LPC_EMAC->IPGT     = EMAC_IPGT_FULL_DUP;
  }
  else
  {
        LPC_EMAC->IPGT = EMAC_IPGT_HALF_DUP;
  }
}
/*********************************************************************//**
 * @brief         Set PHY Speed
 * @param[in]  mode_100Mbps    0: 10Mbps, 1: 100Mbps
 * @return        None
 **********************************************************************/
void EMAC_SetPHYSpeed(uint8_t mode_100Mbps)
{
  if(mode_100Mbps)
  {
     LPC_EMAC->SUPP = EMAC_SUPP_SPEED;
  }
  else
  {
     LPC_EMAC->SUPP = 0;
  }
}


/*********************************************************************//**
 * @brief		Initializes the EMAC peripheral according to the specified
*               parameters in the EMAC_ConfigStruct.
 * @param[in]	EMAC_ConfigStruct Pointer to a EMAC_CFG_Type structure
*                    that contains the configuration information for the
*                    specified EMAC peripheral.
 * @return		None
 *
 * Note: This function will initialize EMAC module according to procedure below:
 *  - Remove the soft reset condition from the MAC
 *  - Configure the PHY via the MIIM interface of the MAC
 *  - Select RMII mode
 *  - Configure the transmit and receive DMA engines, including the descriptor arrays
 *  - Configure the host registers (MAC1,MAC2 etc.) in the MAC
 *  - Enable the receive and transmit data paths
 *  In default state after initializing,  Rx Done and Tx Done interrupt are enabled,
 *  nad all  interrupts are also enabled
 *  (Ref. from LPC17xx UM)
 **********************************************************************/
int32_t EMAC_Init(EMAC_CFG_Type *EMAC_ConfigStruct)
{
	/* Initialize the EMAC Ethernet controller. */
	volatile int32_t tout, tmp;

	EMAC_Configs = *EMAC_ConfigStruct;

	/* Set up power for Ethernet module */
	CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, ENABLE);

	/* Enable P1 Ethernet Pins. */

	/* Reset all EMAC internal modules */
	LPC_EMAC->MAC1    = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
					EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;

	LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES | EMAC_CR_PASS_RUNT_FRM;

	/* A short delay after reset. */
	for (tout = 100; tout; tout--);

	/* Initialize MAC control registers. */
	LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;
	LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN;
	LPC_EMAC->MAXF = EMAC_ETH_MAX_FLEN;
	/*
	 * Find the clock that close to desired target clock
	 */
	tmp = CLKPWR_GetCLK(CLKPWR_CLKTYPE_CPU)/ EMAC_MCFG_MII_MAXCLK;
	for (tout = 0; tout < sizeof (EMAC_clkdiv); tout++)
	{
		if (EMAC_clkdiv[tout] >= tmp)
			break;
	}

        if(tout >= sizeof (EMAC_clkdiv))
		return ERROR;
	tout++;

	// Set Frame size
	LPC_EMAC->MAXF = EMAC_ConfigStruct->nMaxFrameSize;

	// Write to MAC configuration register and reset
	LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(tout) | EMAC_MCFG_RES_MII;

	// release reset
	LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);
	LPC_EMAC->CLRT = EMAC_CLRT_DEF;
	LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;

	/* Enable Reduced MII interface. */
	LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;


	// Initilialize PHY
	if(EMAC_ConfigStruct->pfnPHYInit != NULL)
	{
	    if(EMAC_ConfigStruct->pfnPHYInit(&EMAC_ConfigStruct->PhyCfg) != SUCCESS)
	    {
		return ERROR;
	    }
	}

	// Set EMAC address
	setEmacAddr(EMAC_ConfigStruct->pbEMAC_Addr);

	/* Initialize Tx and Rx DMA Descriptors */
	rx_descr_init ();
	tx_descr_init ();

	// Set Receive Filter register: enable broadcast and multicast
	LPC_EMAC->RxFilterCtrl = EMAC_RFC_MCAST_EN | EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;

	/* Enable Rx Done and Tx Done interrupt for EMAC */
	EMAC_IntCmd((EMAC_INT_RX_OVERRUN | EMAC_INT_RX_ERR | EMAC_INT_RX_FIN \
			| EMAC_INT_RX_DONE | EMAC_INT_TX_UNDERRUN | EMAC_INT_TX_ERR \
			| EMAC_INT_TX_FIN | EMAC_INT_TX_DONE), ENABLE);

	/* Reset all interrupts */
	LPC_EMAC->IntClear  = 0xFFFF;

	/* Enable receive and transmit mode of MAC Ethernet core */
	EMAC_TxEnable();
	EMAC_RxEnable();

	NVIC_EnableIRQ(ENET_IRQn);

	return SUCCESS;
}


/*********************************************************************//**
 * @brief		De-initializes the EMAC peripheral registers to their
*                  default reset values.
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void EMAC_DeInit(void)
{
	// Disable all interrupt
	LPC_EMAC->IntEnable = 0x00;

	// Clear all pending interrupt
	LPC_EMAC->IntClear = (0xFF) | (EMAC_INT_SOFT_INT | EMAC_INT_WAKEUP);

	LPC_EMAC->Command = 0;

	/* TurnOff power for Ethernet module */
	CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, DISABLE);
}

/*********************************************************************//**
 * @brief		EMAC TX API modules
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void EMAC_TxEnable( void )
{
	LPC_EMAC->Command |= EMAC_CR_TX_EN;
	return;
}


/*********************************************************************//**
 * @brief		EMAC RX API modules
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void EMAC_TxDisable( void )
{
	LPC_EMAC->Command &= ~EMAC_CR_TX_EN;
	return;
}

/*********************************************************************//**
 * @brief		EMAC RX API modules
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void EMAC_RxEnable( void )
{
	LPC_EMAC->Command |= EMAC_CR_RX_EN;
	LPC_EMAC->MAC1 |= EMAC_MAC1_REC_EN;
	return;
}

/*********************************************************************//**
 * @brief		EMAC RX API modules
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void EMAC_RxDisable( void )
{
	LPC_EMAC->Command &= ~EMAC_CR_RX_EN;
	LPC_EMAC->MAC1 &= ~EMAC_MAC1_REC_EN;
	return;
}

/*********************************************************************//**
 * @brief		Get the status of  given buffer.
 * @param[in]	 idx   Buffer index
 * @return	 EMAC_BUFF_AVAILABLE/EMAC_BUFF_FULL/EMAC_BUFF_PARTIAL_FULL
 *
 **********************************************************************/
EMAC_BUFF_STATUS EMAC_GetBufferSts(EMAC_BUFF_IDX idx)
{
    uint32_t consume_idx, produce_idx;
    uint32_t max_frag_num;

    // Get the consume index, produce index and the buffer size
    if(idx == EMAC_TX_BUFF)
    {
        consume_idx = LPC_EMAC->TxConsumeIndex;
        produce_idx = LPC_EMAC->TxProduceIndex;
        max_frag_num = LPC_EMAC->TxDescriptorNumber + 1;
    }
    else
    {
        consume_idx = LPC_EMAC->RxConsumeIndex;
        produce_idx = LPC_EMAC->RxProduceIndex;
        max_frag_num = LPC_EMAC->RxDescriptorNumber + 1;
    }

    // empty
    if(consume_idx == produce_idx)
        return EMAC_BUFF_EMPTY;

    // Full
    if(consume_idx == 0 &&
        produce_idx == max_frag_num - 1)
        return EMAC_BUFF_FULL;
	
    // Wrap-around
    if(consume_idx == produce_idx + 1)
        return EMAC_BUFF_FULL;	

    return EMAC_BUFF_PARTIAL_FULL;
}

/*********************************************************************//**
 * @brief		Allocate a descriptor for sending frame and get the coressponding buffer address
 * @param[in]	FrameSize		The size of frame you want to send
 * @return	Address of the TX_DESC_PACKET buffer
 **********************************************************************/
 uint32_t EMAC_AllocTxBuff(uint16_t nFrameSize, uint8_t bLastFrame)
{
    uint32_t idx;
    uint32_t  dp;
    volatile uint32_t i;

    idx = LPC_EMAC->TxProduceIndex;

    while(EMAC_GetBufferSts(EMAC_TX_BUFF) == EMAC_BUFF_FULL)
    {
      for(i = 0; i < 1000000; i++) ; 
    }

    dp = TX_DESC_PACKET(idx);

    if(bLastFrame)
      TX_DESC_CTRL(idx) = ((nFrameSize-1) & EMAC_TCTRL_SIZE) | (EMAC_TCTRL_INT | EMAC_TCTRL_LAST);
    else
      TX_DESC_CTRL(idx) = ((nFrameSize-1) & EMAC_TCTRL_SIZE) | (EMAC_TCTRL_INT);

    return dp;
}

/*********************************************************************//**
 * @brief	Increase the TxProduceIndex (after writting to the Transmit buffer
 * 		to enable the Transmit buffer) and wrap-around the index if
 * 			it reaches the maximum Transmit Number
 * @param[in]	None
 * @return		None
 **********************************************************************/
void EMAC_UpdateTxProduceIndex(void)
{
	// Get current Tx produce index
	uint32_t idx = LPC_EMAC->TxProduceIndex;

	/* Start frame transmission */
	if (++idx == LPC_EMAC->TxDescriptorNumber + 1) idx = 0;
	LPC_EMAC->TxProduceIndex = idx;
}

/*********************************************************************//**
 * @brief	Get current status value of receive data (due to TxProduceIndex)
 * @param[in]	None	
 * @return	Current value of receive data (due to TxProduceIndex)
 **********************************************************************/
uint32_t EMAC_GetTxFrameStatus(void)
{
	uint32_t idx;

	idx = LPC_EMAC->TxProduceIndex;
	return (TX_STAT_INFO(idx));
}

/*********************************************************************//**
 * @brief	Write data to Tx packet data buffer at current index due to
 * 				TxProduceIndex
 * @param[in]	pDataStruct	store the address and the size of buffer that saves data.
 * @return		None
 **********************************************************************/
void EMAC_WritePacketBuffer(EMAC_PACKETBUF_Type *pDataStruct)
{
   uint16_t* pDest;
   uint16_t* pSource = (uint16_t*)pDataStruct->pbDataBuf;
   uint32_t  size = pDataStruct->ulDataLen;
   int32_t  frame_num;
   uint32_t tmp;
   uint32_t max_frame_size = LPC_EMAC->MAXF;
   
   size = (size + 1) & 0xFFFE;    // round Size up to next even number
   frame_num = size/max_frame_size;

   if(size == 0)
   	return;

   while(frame_num >= 0)
   {
     tmp = (frame_num > 0)? max_frame_size:size;

     if(tmp == 0)
	    break;
	 
     // Setup descriptors and data
     if(frame_num == 0)
       pDest = (uint16_t*)EMAC_AllocTxBuff(tmp, 1);  // last frame
     else
       pDest = (uint16_t*)EMAC_AllocTxBuff(tmp, 0);
	 
     // Copy data
     while (tmp > 0)
     {
       *pDest++ = *pSource++;
       tmp -= 2;
     }
     frame_num--;
     size -= tmp;

     // Update produce index
     EMAC_UpdateTxProduceIndex();
   } 
}


/*********************************************************************//**
 * @brief		Get current status value of receive data (due to RxConsumeIndex)
 * @param[in]	None	
 * @return	Current value of receive data (due to RxConsumeIndex)
 **********************************************************************/
uint32_t EMAC_GetRxFrameStatus(void)
{
	uint32_t idx;

	idx = LPC_EMAC->RxConsumeIndex;
	return (RX_STAT_INFO(idx));
}


/*********************************************************************//**
 * @brief		Get size of current Received data in received buffer (due to
 * 				RxConsumeIndex)
 * @param[in]	None
 * @return		Size of received data
 **********************************************************************/
uint32_t EMAC_GetRxFrameSize(void)
{
	uint32_t idx;

	idx = LPC_EMAC->RxConsumeIndex;

	return (((RX_STAT_INFO(idx)) & EMAC_RINFO_SIZE)+1);
}


/*********************************************************************//**
 * @brief		Get the address of TX_DESC_PACKET buffer so that user can access from application
 * @param[in] None
 * @return	Address of the TX_DESC_PACKET buffer
 **********************************************************************/

uint32_t EMAC_GetRxBuffer(void)
{
	uint32_t idx;

	idx = LPC_EMAC->RxConsumeIndex;

  	return RX_DESC_PACKET(idx);
 }


/*********************************************************************//**
 * @brief		Increase the RxConsumeIndex (after reading the Receive buffer
 * 				to release the Receive buffer) and wrap-around the index if
 * 				it reaches the maximum Receive Number
 * @param[in]	None
 * @return		None
 **********************************************************************/
void EMAC_UpdateRxConsumeIndex(void)
{
	// Get current Rx consume index
	uint32_t idx = LPC_EMAC->RxConsumeIndex;

	/* Release frame from EMAC buffer */
	if (++idx == EMAC_NUM_RX_FRAG) idx = 0;

	LPC_EMAC->RxConsumeIndex = idx;
}

/*********************************************************************//**
 * @brief		Standard EMAC IRQ Handler. This sub-routine will check
 * 				these following interrupt and call the call-back function
 * 				if they're already installed:
 * 				- Overrun Error interrupt in RX Queue
 * 				- Receive Error interrupt: AlignmentError, RangeError,
 * 				LengthError, SymbolError, CRCError or NoDescriptor or Overrun
 * 				- RX Finished Process Descriptors interrupt (ProduceIndex == ConsumeIndex)
 * 				- Receive Done interrupt: Read received frame to the internal buffer
 * 				- Transmit Under-run interrupt
 * 				- Transmit errors interrupt : LateCollision, ExcessiveCollision
 * 				and ExcessiveDefer, NoDescriptor or Under-run
 * 				- TX Finished Process Descriptors interrupt (ProduceIndex == ConsumeIndex)
 * 				- Transmit Done interrupt
 * 				- Interrupt triggered by software
 *				- Interrupt triggered by a Wakeup event detected by the receive filter
 * @param[in]	None
 * @return		None
 **********************************************************************/
void ENET_IRQHandler(void)
{
	/* EMAC Ethernet Controller Interrupt function. */
	uint32_t int_stat;
	int32_t RxLen;
	
	// Get EMAC interrupt status
	while ((int_stat = (LPC_EMAC->IntStatus & LPC_EMAC->IntEnable)) != 0)
	{
		// Clear interrupt status
		LPC_EMAC->IntClear = int_stat;
	 
	    if(int_stat & (EMAC_INT_RX_OVERRUN |EMAC_INT_RX_ERR ))
		{
		   uint32_t ulFrameSts = EMAC_GetRxFrameStatus();
		   uint32_t ulErrCode = 0;

		   ulErrCode |= (ulFrameSts & EMAC_RINFO_CRC_ERR) ? EMAC_CRC_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_SYM_ERR) ? EMAC_SYMBOL_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_LEN_ERR) ? EMAC_LENGTH_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_ALIGN_ERR) ? EMAC_ALIGN_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_OVERRUN) ? EMAC_OVERRUN_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_NO_DESCR) ? EMAC_RX_NO_DESC_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_RINFO_FAIL_FILT) ? EMAC_FILTER_FAILED_ERR:0;
		   
		   if(ulErrCode == 0)
		   {
		      /* Note:
		          * The EMAC doesn't distinguish the frame type and frame length,
		          * so, e.g. when the IP(0x8000) or ARP(0x0806) packets are received,
		          * it compares the frame type with the max length and gives the
		          * "Range" error. In fact, this bit is not an error indication,
		          * but simply a statement by the chip regarding the status of
		          * the received frame
		          */
		      int_stat &= ~EMAC_INT_RX_ERR;
		   }
		   else
		   {
		     if(EMAC_Configs.pfnErrorReceive != NULL)
		       EMAC_Configs.pfnErrorReceive(ulErrCode);
		   	}
		}

		if(int_stat & (EMAC_INT_TX_UNDERRUN|EMAC_INT_TX_ERR ))
		{
		   uint32_t ulFrameSts = EMAC_GetTxFrameStatus();
		   uint32_t ulErrCode = 0;

		   ulErrCode |= (ulFrameSts & EMAC_TINFO_EXCESS_DEF) ? EMAC_EXCESSIVE_DEFER_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_TINFO_EXCESS_COL) ? EMAC_EXCESSIVE_COLLISION_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_TINFO_LATE_COL) ? EMAC_LATE_COLLISION_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_TINFO_UNDERRUN) ? EMAC_UNDERRUN_ERR:0;
		   ulErrCode |= (ulFrameSts & EMAC_TINFO_NO_DESCR) ? EMAC_TX_NO_DESC_ERR:0;
		   if(EMAC_Configs.pfnErrorReceive != NULL)
		      EMAC_Configs.pfnErrorReceive(ulErrCode);
		}
   
	    if(int_stat & EMAC_INT_RX_DONE)
	    {

	      /* Packet received. */
	      if(EMAC_GetBufferSts(EMAC_RX_BUFF) != EMAC_BUFF_EMPTY)
	      {
	         // Get data size
		 RxLen = EMAC_GetRxFrameSize();
		 if(RxLen > 0)
		 {
		    // trip out 4-bytes CRC field, note that length in (-1) style format
		    RxLen -= 3;
			  
		    if((EMAC_GetRxFrameStatus() & EMAC_RINFO_ERR_MASK )== 0)
                    {
			uint16_t *pDest = (uint16_t*) &saFrameBuffers[sbCurrFrameID][sulCurrFrameSz];
			uint16_t *pSource = (uint16_t*)EMAC_GetRxBuffer();
                        sulCurrFrameSz += RxLen;
				 
			if(sulCurrFrameSz >= EMAC_MAX_FRAME_SIZE)
			{
			  sulCurrFrameSz = 0;
			  if(EMAC_Configs.pfnErrorReceive != NULL)
		             EMAC_Configs.pfnErrorReceive(EMAC_LENGTH_ERR);
			}
			else
			{
			    // Copy data
			   while (RxLen > 0)
			    {
	                        *pDest++ = *pSource++;
	                        RxLen -= 2;
                            } 
			    if(EMAC_GetRxFrameStatus() & EMAC_RINFO_LAST_FLAG)
			    {
				  
			        if(EMAC_Configs.pfnFrameReceive != NULL)
			        {
				  EMAC_Configs.pfnFrameReceive((uint16_t*)saFrameBuffers[sbCurrFrameID], sulCurrFrameSz);
			        }
			        sulCurrFrameSz = 0;
			        sbCurrFrameID ++;
			        if(sbCurrFrameID >= EMAC_MAX_FRAME_NUM)
				    sbCurrFrameID = 0;
			    }	
			    /* Release frame from EMAC buffer */
			    EMAC_UpdateRxConsumeIndex();
			}
		    }
		}			 
	    }
 	    
	}
		
        if(int_stat & EMAC_INT_TX_FIN && (EMAC_Configs.pfnTransmitFinish != NULL))
        {
		   	 EMAC_Configs.pfnTransmitFinish();
        }

		if(int_stat & EMAC_INT_SOFT_INT && (EMAC_Configs.pfnSoftInt!= NULL))
        {
		   	 EMAC_Configs.pfnSoftInt();
        }

		if(int_stat & EMAC_INT_WAKEUP && (EMAC_Configs.pfnWakeup!= NULL))
        {
		   	 EMAC_Configs.pfnWakeup();
        }
	 
    }
}


/*********************************************************************//**
 * @brief		Enable/Disable hash filter functionality for specified destination
 * 				MAC address in EMAC module
 * @param[in]	dstMAC_addr    Pointer to the first MAC destination address, should
 * 					be 6-bytes length, in order LSB to the MSB
 * @param[in]	NewState        New State of this command, should be:
 *					- ENABLE.
 *					- DISABLE.
 * @return		None
 *
 * Note:
 * The standard Ethernet cyclic redundancy check (CRC) function is calculated from
 * the 6 byte destination address in the Ethernet frame (this CRC is calculated
 * anyway as part of calculating the CRC of the whole frame), then bits [28:23] out of
 * the 32 bits CRC result are taken to form the hash. The 6 bit hash is used to access
 * the hash table: it is used as an index in the 64 bit HashFilter register that has been
 * programmed with accept values. If the selected accept value is 1, the frame is
 * accepted.
 **********************************************************************/
void EMAC_SetHashFilter(uint8_t dstMAC_addr[], FunctionalState NewState)
{
	uint32_t *pReg;
	uint32_t tmp;
	int32_t crc;

	// Calculate the CRC from the destination MAC address
	crc = EMAC_CRCCalc(dstMAC_addr, 6);
	// Extract the value from CRC to get index value for hash filter table
	crc = (crc >> 23) & 0x3F;

	pReg = (crc > 31) ? ((uint32_t *)&LPC_EMAC->HashFilterH) \
						: ((uint32_t *)&LPC_EMAC->HashFilterL);
	tmp = (crc > 31) ? (crc - 32) : crc;
	if (NewState == ENABLE)
	{
		(*pReg) |= (1UL << tmp);
	}
	else
	{
		(*pReg) &= ~(1UL << tmp);
	}
	
}


/*********************************************************************//**
 * @brief		Calculates CRC code for number of bytes in the frame
 * @param[in]	frame_no_fcs	Pointer to the first byte of the frame
 * @param[in]	frame_len		length of the frame without the FCS
 * @return		the CRC as a 32 bit integer
 **********************************************************************/
int32_t EMAC_CRCCalc(uint8_t frame_no_fcs[], int32_t frame_len)
{
	int i; 		// iterator
	int j; 		// another iterator
	char byte; 	// current byte
	int crc; 	// CRC result
	int q0, q1, q2, q3; // temporary variables

	crc = 0xFFFFFFFF;

	for (i = 0; i < frame_len; i++)
	{
		byte = *frame_no_fcs++;
		for (j = 0; j < 2; j++)
		{
			if (((crc >> 28) ^ (byte >> 3)) & 0x00000001)
			{
				q3 = 0x04C11DB7;
			}
			else
			{
				q3 = 0x00000000;
			}

			if (((crc >> 29) ^ (byte >> 2)) & 0x00000001)
			{
				q2 = 0x09823B6E;
			}
			else
			{
				q2 = 0x00000000;
			}

			if (((crc >> 30) ^ (byte >> 1)) & 0x00000001)
			{
				q1 = 0x130476DC;
			}
			else
			{
				q1 = 0x00000000;
			}

			if (((crc >> 31) ^ (byte >> 0)) & 0x00000001)
			{
				q0 = 0x2608EDB8;
			}
			else
			{
				q0 = 0x00000000;
			}

			crc = (crc << 4) ^ q3 ^ q2 ^ q1 ^ q0;

			byte >>= 4;
		}
	}

	return crc;
}

/*********************************************************************//**
 * @brief		Enable/Disable Filter mode for each specified type EMAC peripheral
 * @param[in]	ulFilterMode	Filter mode, should be:
 * 								- EMAC_RFC_UCAST_EN: all frames of unicast types
 * 								will be accepted
 * 								- EMAC_RFC_BCAST_EN: broadcast frame will be
 * 								accepted
 * 								- EMAC_RFC_MCAST_EN: all frames of multicast
 * 								types will be accepted
 * 								- EMAC_RFC_UCAST_HASH_EN: The imperfect hash
 * 								filter will be applied to unicast addresses
 * 								- EMAC_RFC_MCAST_HASH_EN: The imperfect hash
 * 								filter will be applied to multicast addresses
 * 								- EMAC_RFC_PERFECT_EN: the destination address
 * 								will be compared with the 6 byte station address
 * 								programmed in the station address by the filter
 * 								- EMAC_RFC_MAGP_WOL_EN: the result of the magic
 * 								packet filter will generate a WoL interrupt when
 * 								there is a match
 * 								- EMAC_RFC_PFILT_WOL_EN: the result of the perfect address
 * 								matching filter and the imperfect hash filter will
 * 								generate a WoL interrupt when there is a match
 * @param[in]	NewState	New State of this command, should be:
 * 								- ENABLE
 * 								- DISABLE
 * @return		None
 **********************************************************************/
void EMAC_SetFilterMode(uint32_t ulFilterMode, FunctionalState NewState)
{
	if (NewState == ENABLE)
	{
		LPC_EMAC->RxFilterCtrl |= ulFilterMode;

		if((ulFilterMode & EMAC_RFC_MCAST_HASH_EN) &&
			((ulFilterMode & EMAC_RFC_MCAST_EN) == 0))
		{
		  LPC_EMAC->RxFilterCtrl &= ~EMAC_RFC_MCAST_EN;
		}
		if((ulFilterMode & EMAC_RFC_UCAST_HASH_EN) &&
			((ulFilterMode & EMAC_RFC_UCAST_EN) == 0))
		{
		  LPC_EMAC->RxFilterCtrl &= ~EMAC_RFC_UCAST_EN;
		}
	}
	else
	{
		LPC_EMAC->RxFilterCtrl &= ~ulFilterMode;
	}
}

/*********************************************************************//**
 * @brief		Get status of Wake On LAN Filter for each specified
 * 				type in EMAC peripheral, clear this status if it is set
 * @param[in]	ulFilterMode	WoL Filter mode, should be:
 * 								- EMAC_WOL_UCAST: unicast frames caused WoL
 * 								- EMAC_WOL_UCAST: broadcast frame caused WoL
 * 								- EMAC_WOL_MCAST: multicast frame caused WoL
 * 								- EMAC_WOL_UCAST_HASH: unicast frame that passes the
 * 								imperfect hash filter caused WoL
 * 								- EMAC_WOL_MCAST_HASH: multicast frame that passes the
 * 								imperfect hash filter caused WoL
 * 								- EMAC_WOL_PERFECT:perfect address matching filter
 * 								caused WoL
 * 								- EMAC_WOL_RX_FILTER: the receive filter caused WoL
 * 								- EMAC_WOL_MAG_PACKET: the magic packet filter caused WoL
 * @return		SET/RESET
 **********************************************************************/
FlagStatus EMAC_GetWoLStatus(uint32_t ulWoLMode)
{
	if (LPC_EMAC->RxFilterWoLStatus & ulWoLMode)
	{
		LPC_EMAC->RxFilterWoLClear = ulWoLMode;

		return SET;
	}
	else
	{
		return RESET;
	}
}






/*********************************************************************//**
 * @brief 		Enable/Disable interrupt for each type in EMAC
 * @param[in]	ulIntType	Interrupt Type, should be:
 * 							- EMAC_INT_RX_OVERRUN: Receive Overrun
 * 							- EMAC_INT_RX_ERR: Receive Error
 * 							- EMAC_INT_RX_FIN: Receive Descriptor Finish
 * 							- EMAC_INT_RX_DONE: Receive Done
 * 							- EMAC_INT_TX_UNDERRUN: Transmit Under-run
 * 							- EMAC_INT_TX_ERR: Transmit Error
 * 							- EMAC_INT_TX_FIN: Transmit descriptor finish
 * 							- EMAC_INT_TX_DONE: Transmit Done
 * 							- EMAC_INT_SOFT_INT: Software interrupt
 * 							- EMAC_INT_WAKEUP: Wakeup interrupt
 * @param[in]	NewState	New State of this function, should be:
 * 							- ENABLE.
 * 							- DISABLE.
 * @return		None
 **********************************************************************/
void EMAC_IntCmd(uint32_t ulIntType, FunctionalState NewState)
{
	if (NewState == ENABLE)
	{
		LPC_EMAC->IntEnable |= ulIntType;
	}
	else
	{
		LPC_EMAC->IntEnable &= ~(ulIntType);
	}
}

/*********************************************************************//**
 * @brief 		Check whether if specified interrupt flag is set or not
 * 				for each interrupt type in EMAC and clear interrupt pending
 * 				if it is set.
 * @param[in]	ulIntType	Interrupt Type, should be:
 * 							- EMAC_INT_RX_OVERRUN: Receive Overrun
 * 							- EMAC_INT_RX_ERR: Receive Error
 * 							- EMAC_INT_RX_FIN: Receive Descriptor Finish
 * 							- EMAC_INT_RX_DONE: Receive Done
 * 							- EMAC_INT_TX_UNDERRUN: Transmit Under-run
 * 							- EMAC_INT_TX_ERR: Transmit Error
 * 							- EMAC_INT_TX_FIN: Transmit descriptor finish
 * 							- EMAC_INT_TX_DONE: Transmit Done
 * 							- EMAC_INT_SOFT_INT: Software interrupt
 * 							- EMAC_INT_WAKEUP: Wakeup interrupt
 * @return		New state of specified interrupt (SET or RESET)
 **********************************************************************/
IntStatus EMAC_IntGetStatus(uint32_t ulIntType)
{
	if (LPC_EMAC->IntStatus & ulIntType)
	{
		LPC_EMAC->IntClear = ulIntType;
		return SET;
	}
	else
	{
		return RESET;
	}
}



/**
 * @}
 */

/**
 * @}
 */
#endif /*_EMAC*/
